package com.gitee.l0km.aocache;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.gitee.l0km.aocache.ObjectUtils.STRING_VALUEOF_FUN;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.gitee.l0km.aocache.annotations.AoCacheable;
import com.gitee.l0km.aocache.config.CacheConfig;
import com.google.common.base.CharMatcher;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.reflect.TypeToken;

class AocacheUtils implements AocacheConstant{
    /**
     * <p>Returns new instance of {@code klazz} created using the actual arguments {@code args}.
     * The formal parameter types are inferred from the actual values of {@code args}.
     * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
     *
     * <p>The signatures should be assignment compatible.</p>
     *
     * @param <T> the type of the object to be constructed
     * @param klass the class to be constructed.
     * @param args actual argument array. May be null (this will result in calling the default constructor).
     * @return new instance of {@code klazz}
     *
     * @throws NoSuchMethodException If the constructor cannot be found
     * @throws IllegalAccessException If an error occurs accessing the constructor
     * @throws InvocationTargetException If an error occurs invoking the constructor
     * @throws InstantiationException If an error occurs instantiating the class
     *
     * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
     */
    static <T> T invokeConstructor(Class<T> klass, Object[] args)
        throws
            NoSuchMethodException,
            IllegalAccessException,
            InvocationTargetException,
            InstantiationException {

        if (null == args) {
            args = EMPTY_OBJECT_ARRAY;
        }
        int arguments = args.length;
        Class<?> parameterTypes[] = new Class<?>[arguments];
        for (int i = 0; i < arguments; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        return invokeConstructor(klass, args, parameterTypes);
    }
    /**
     * <p>Returns new instance of {@code klazz} created using constructor
     * with signature {@code parameterTypes} and actual arguments {@code args}.</p>
     *
     * <p>The signatures should be assignment compatible.</p>
     *
     * @param <T> the type of the object to be constructed
     * @param klass the class to be constructed.
     * @param args actual argument array. May be null (this will result in calling the default constructor).
     * @param parameterTypes parameter types array
     * @return new instance of {@code klazz}
     *
     * @throws NoSuchMethodException if matching constructor cannot be found
     * @throws IllegalAccessException thrown on the constructor's invocation
     * @throws InvocationTargetException thrown on the constructor's invocation
     * @throws InstantiationException thrown on the constructor's invocation
     * @see Constructor#newInstance
     */
    static <T> T invokeConstructor(
        Class<T> klass,
        Object[] args,
        Class<?>[] parameterTypes)
        throws
            NoSuchMethodException,
            IllegalAccessException,
            InvocationTargetException,
            InstantiationException {

        if (parameterTypes == null) {
            parameterTypes = EMPTY_CLASS_PARAMETERS;
        }
        if (args == null) {
            args = EMPTY_OBJECT_ARRAY;
        }

        Constructor<T> ctor =
            getMatchingAccessibleConstructor(klass, parameterTypes);
        if (null == ctor) {
            throw new NoSuchMethodException(
                "No such accessible constructor on object: " + klass.getName()+toMemberString(null,parameterTypes));
        }
        return ctor.newInstance(args);
    }
    // -------------------------------------------------------- Private Methods
    /**
     * <p>Find an accessible constructor with compatible parameters.
     * Compatible parameters mean that every method parameter is assignable from
     * the given parameters. In other words, it finds constructor that will take
     * the parameters given.</p>
     *
     * <p>First it checks if there is constructor matching the exact signature.
     * If no such, all the constructors of the class are tested if their signatures
     * are assignment compatible with the parameter types.
     * The first matching constructor is returned.</p>
     *
     * @param <T> the type of the class to be inspected
     * @param clazz find constructor for this class
     * @param parameterTypes find method with compatible parameters
     * @return a valid Constructor object. If there's no matching constructor, returns {@code null}.
     */
    private static <T> Constructor<T> getMatchingAccessibleConstructor(
        Class<T> clazz,
        Class<?>[] parameterTypes) {
        // see if we can find the method directly
        // most of the time this works and it's much faster
        try {
            Constructor<T> ctor = clazz.getConstructor(parameterTypes);
            try {
                //
                // XXX Default access superclass workaround
                //
                // When a public class has a default access superclass
                // with public methods, these methods are accessible.
                // Calling them from compiled code works fine.
                //
                // Unfortunately, using reflection to invoke these methods
                // seems to (wrongly) to prevent access even when the method
                // modifer is public.
                //
                // The following workaround solves the problem but will only
                // work from sufficiently privilages code.
                //
                // Better workarounds would be greatfully accepted.
                //
                ctor.setAccessible(true);
            } catch (SecurityException se) {
                /* SWALLOW, if workaround fails don't fret. */
            }
            return ctor;

        } catch (NoSuchMethodException e) { /* SWALLOW */
        }

        // search through all methods
        int paramSize = parameterTypes.length;
        Constructor<?>[] ctors = clazz.getDeclaredConstructors();
        for (int i = 0, size = ctors.length; i < size; i++) {
            // compare parameters
            Class<?>[] ctorParams = ctors[i].getParameterTypes();
            int ctorParamSize = ctorParams.length;
            if (ctorParamSize == paramSize) {
                boolean match = true;
                for (int n = 0; n < ctorParamSize; n++) {
                    if (!isAssignmentCompatible(
                            ctorParams[n],
                            parameterTypes[n])) {
                        match = false;
                        break;
                    }
                }

                if (match) {
                    // get accessible version of method
                    Constructor<?> ctor = ctors[i];
                    if (ctor != null) {
                        try {
                            ctor.setAccessible(true);
                        } catch (SecurityException se) {
                            /* Swallow SecurityException
                             * TODO: Why?
                             */
                        }
                        @SuppressWarnings("unchecked")
                        // Class.getConstructors() actually returns constructors
                        // of type T, so it is safe to cast.
                        Constructor<T> typedCtor = (Constructor<T>) ctor;
                        return typedCtor;
                    }
                }
            }
        }

        return null;
    }
    /**
     * <p>Determine whether a type can be used as a parameter in a method invocation.
     * This method handles primitive conversions correctly.</p>
     *
     * <p>In order words, it will match a {@code Boolean} to a {@code boolean},
     * a {@code Long} to a {@code long},
     * a {@code Float} to a {@code float},
     * a {@code Integer} to a {@code int},
     * and a {@code Double} to a {@code double}.
     * Now logic widening matches are allowed.
     * For example, a {@code Long} will not match a {@code int}.
     *
     * @param parameterType the type of parameter accepted by the method
     * @param parameterization the type of parameter being tested
     *
     * @return true if the assignment is compatible.
     */
    private static final boolean isAssignmentCompatible(Class<?> parameterType, Class<?> parameterization) {
        // try plain assignment
        if (parameterType.isAssignableFrom(parameterization)) {
            return true;
        }

        if (parameterType.isPrimitive()) {
            // this method does *not* do widening - you must specify exactly
            // is this the right behaviour?
            Class<?> parameterWrapperClazz = getPrimitiveWrapper(parameterType);
            if (parameterWrapperClazz != null) {
                return parameterWrapperClazz.equals(parameterization);
            }
        }

        return false;
    }
	/**
	 * <p>Invoke a named method whose parameter type matches the object type.</p>
	 *
	 * <p>The behaviour of this method is less deterministic
	 * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
	 * It loops through all methods with names that match
	 * and then executes the first it finds with compatible parameters.</p>
	 *
	 * <p>This method supports calls to methods taking primitive parameters
	 * via passing in wrapping classes. So, for example, a {@code Boolean} class
	 * would match a {@code boolean} primitive.</p>
	 *
	 * <p> This is a convenient wrapper for
	 * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
	 * </p>
	 *
	 * @param object invoke method on this object
	 * @param methodName get method with this name
	 * @param args use these arguments - treat null as empty array (passing null will
	 *  result in calling the parameterless method with name {@code methodName}).
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException if there is no such accessible method
	 * @throws InvocationTargetException wraps an exception thrown by the
	 *  method invoked
	 * @throws IllegalAccessException if the requested method is not accessible
	 *  via reflection
	 */
	static Object invokeMethod(
	        Object object,
	        String methodName,
	        Object[] args)
	        throws
	        NoSuchMethodException,
	        IllegalAccessException,
	        InvocationTargetException {
	
	    if (args == null) {
	        args = EMPTY_OBJECT_ARRAY;
	    }
	    int arguments = args.length;
	    Class<?>[] parameterTypes = new Class[arguments];
	    for (int i = 0; i < arguments; i++) {
	        parameterTypes[i] = args[i].getClass();
	    }
	    return invokeMethod(object, methodName, args, parameterTypes);
	}
	/**
	 * <p>Invoke a named method whose parameter type matches the object type.</p>
	 *
	 * <p>The behaviour of this method is less deterministic
	 * than {@link
	 * #invokeExactMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
	 * It loops through all methods with names that match
	 * and then executes the first it finds with compatible parameters.</p>
	 *
	 * <p>This method supports calls to methods taking primitive parameters
	 * via passing in wrapping classes. So, for example, a {@code Boolean} class
	 * would match a {@code boolean} primitive.</p>
	 *
	 *
	 * @param object invoke method on this object
	 * @param methodName get method with this name
	 * @param args use these arguments - treat null as empty array (passing null will
	 *  result in calling the parameterless method with name {@code methodName}).
	 * @param parameterTypes match these parameters - treat null as empty array
	 * @return The value returned by the invoked method
	 *
	 * @throws NoSuchMethodException if there is no such accessible method
	 * @throws InvocationTargetException wraps an exception thrown by the
	 *  method invoked
	 * @throws IllegalAccessException if the requested method is not accessible
	 *  via reflection
	 */
	static Object invokeMethod(
			Object object,
			String methodName,
			Object[] args,
			Class<?>[] parameterTypes)
					throws
					NoSuchMethodException,
					IllegalAccessException,
					InvocationTargetException {
		
		if (parameterTypes == null) {
			parameterTypes = EMPTY_CLASS_PARAMETERS;
		}
		if (args == null) {
			args = EMPTY_OBJECT_ARRAY;
		}
		
		Method method = getMatchingAccessibleMethod(
				object.getClass(),
				methodName,
				parameterTypes);
		if (method == null) {
			throw new NoSuchMethodException("No such accessible method: " +
					methodName + "() on object: " + object.getClass().getName()+" with matched parameter type: "+toMemberString(methodName,parameterTypes));
		}
		return method.invoke(object, args);
	}
    /**
     * <p>Find an accessible method that matches the given name and has compatible parameters.
     * Compatible parameters mean that every method parameter is assignable from
     * the given parameters.
     * In other words, it finds a method with the given name
     * that will take the parameters given.<p>
     *
     * <p>This method is slightly undeterministic since it loops
     * through methods names and return the first matching method.</p>
     *
     * <p>This method is used by
     * {@link
     * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
     *
     * <p>This method can match primitive parameter by passing in wrapper classes.
     * For example, a {@code Boolean} will match a primitive {@code boolean}
     * parameter.
     *
     * @param clazz find method in this class
     * @param methodName find method with this name
     * @param parameterTypes find method with compatible parameters
     * @return The accessible method
     */
    private static Method getMatchingAccessibleMethod(
                                                Class<?> clazz,
                                                String methodName,
                                                Class<?>[] parameterTypes) {

        // see if we can find the method directly
        // most of the time this works and it's much faster
        try {

            Method method = clazz.getMethod(methodName, parameterTypes);

            setMethodAccessible(method); // Default access superclass workaround

            return method;

        } catch (NoSuchMethodException e) { /* SWALLOW */ }

        // search through all methods
        int paramSize = parameterTypes.length;
        Method bestMatch = null;
        Method[] methods = allDeclaredMethodOf(clazz);
        float bestMatchCost = Float.MAX_VALUE;
        float myCost = Float.MAX_VALUE;
        for (int i = 0, size = methods.length; i < size ; i++) {
            if (methods[i].getName().equals(methodName)) {

                // compare parameters
                Class<?>[] methodsParams = methods[i].getParameterTypes();
                int methodParamSize = methodsParams.length;
                if (methodParamSize == paramSize) {
                    boolean match = true;
                    for (int n = 0 ; n < methodParamSize; n++) {
                        if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
                            match = false;
                            break;
                        }
                    }

                    if (match) {
                        // get accessible version of method
                        Method method = methods[i];
                        if (method != null) {
                            setMethodAccessible(method); // Default access superclass workaround
                            myCost = getTotalTransformationCost(parameterTypes,method.getParameterTypes());
                            if ( myCost < bestMatchCost ) {
                               bestMatch = method;
                               bestMatchCost = myCost;
                            }
                        }
                    }
                }
            }
        }
        return bestMatch;
    }
    // Superclasses/Superinterfaces
    // ----------------------------------------------------------------------
    /**
     * <p>Gets a {@code List} of superclasses for the given class.</p>
     *
     * @param cls  the class to look up, may be {@code null}
     * @return the {@code List} of superclasses in order going up from this one
     *  {@code null} if null input
     */
    private static List<Class<?>> getAllSuperclasses(final Class<?> cls) {
        if (cls == null) {
            return null;
        }
        final List<Class<?>> classes = new ArrayList<>();
        Class<?> superclass = cls.getSuperclass();
        while (superclass != null) {
            classes.add(superclass);
            superclass = superclass.getSuperclass();
        }
        return classes;
    }
    private static Method[] allDeclaredMethodOf(Class<?> clazz) {
        // Address methods in superclasses
        FluentIterable<Method> methods = FluentIterable.from(Lists.newArrayList(clazz.getDeclaredMethods()));
        for (final Class<?> superClass : getAllSuperclasses(clazz)) {
        	methods = methods.append(superClass.getDeclaredMethods());
        }
        return methods.toArray(Method.class);
    }
    /**
     * Try to make the method accessible
     * @param method The source arguments
     */
    private static void setMethodAccessible(Method method) {
    	if (!Modifier.isPublic(method.getModifiers()) && !method.isAccessible()) {
    		method.setAccessible(true);
		}
    }
    /**
     * Returns the sum of the object transformation cost for each class in the source
     * argument list.
     * @param srcArgs The source arguments
     * @param destArgs The destination arguments
     * @return The total transformation cost
     */
    private static float getTotalTransformationCost(Class<?>[] srcArgs, Class<?>[] destArgs) {

        float totalCost = 0.0f;
        for (int i = 0; i < srcArgs.length; i++) {
            Class<?> srcClass, destClass;
            srcClass = srcArgs[i];
            destClass = destArgs[i];
            totalCost += getObjectTransformationCost(srcClass, destClass);
        }

        return totalCost;
    }

    /**
     * Gets the number of steps required needed to turn the source class into the
     * destination class. This represents the number of steps in the object hierarchy
     * graph.
     * @param srcClass The source class
     * @param destClass The destination class
     * @return The cost of transforming an object
     */
    private static float getObjectTransformationCost(Class<?> srcClass, Class<?> destClass) {
        float cost = 0.0f;
        while (srcClass != null && !destClass.equals(srcClass)) {
            if (destClass.isPrimitive()) {
                Class<?> destClassWrapperClazz = getPrimitiveWrapper(destClass);
                if (destClassWrapperClazz != null && destClassWrapperClazz.equals(srcClass)) {
                    cost += 0.25f;
                    break;
                }
            }
            if (destClass.isInterface() && isAssignmentCompatible(destClass,srcClass)) {
                // slight penalty for interface match.
                // we still want an exact match to override an interface match, but
                // an interface match should override anything where we have to get a
                // superclass.
                cost += 0.25f;
                break;
            }
            cost++;
            srcClass = srcClass.getSuperclass();
        }

        /*
         * If the destination class is null, we've travelled all the way up to
         * an Object match. We'll penalize this by adding 1.5 to the cost.
         */
        if (srcClass == null) {
            cost += 1.5f;
        }

        return cost;
    }
    /**
     * Gets the wrapper object class for the given primitive type class.
     * For example, passing {@code boolean.class} returns {@code Boolean.class}
     * @param primitiveType the primitive type class for which a match is to be found
     * @return the wrapper type associated with the given primitive
     * or null if no match is found
     */
    private static Class<?> getPrimitiveWrapper(Class<?> primitiveType) {
        // does anyone know a better strategy than comparing names?
        if (boolean.class.equals(primitiveType)) {
            return Boolean.class;
        } else if (float.class.equals(primitiveType)) {
            return Float.class;
        } else if (long.class.equals(primitiveType)) {
            return Long.class;
        } else if (int.class.equals(primitiveType)) {
            return Integer.class;
        } else if (short.class.equals(primitiveType)) {
            return Short.class;
        } else if (byte.class.equals(primitiveType)) {
            return Byte.class;
        } else if (double.class.equals(primitiveType)) {
            return Double.class;
        } else if (char.class.equals(primitiveType)) {
            return Character.class;
        } else {

            return null;
        }
    }
    private static String typeNameOf(Class<?> clazz) {
        if (clazz.isArray()) {
            try {
                Class<?> cl = clazz;
                int dimensions = 0;
                while (cl.isArray()) {
                    dimensions++;
                    cl = cl.getComponentType();
                }
                StringBuilder sb = new StringBuilder();
                sb.append(cl.getName());
                for (int i = 0; i < dimensions; i++) {
                    sb.append("[]");
                }
                return sb.toString();
            } catch (Throwable e) { /*FALLTHRU*/ }
        }
        return clazz.getName();
    }
    static String toMemberString(String name, Class<?>[]parameterTypes) {
    	try {
            StringBuilder sb = new StringBuilder();
            if(null!=name) {
            	sb.append(name);
            }
            sb.append('(');
            for (int j = 0; j < parameterTypes.length; j++) {
                sb.append(typeNameOf(parameterTypes[j]));
                if (j < (parameterTypes.length - 1))
                    sb.append(",");
            }
            sb.append(')');
            return sb.toString();
        } catch (Exception e) {
            return "<" + e + ">";
        }
    }
	/**
	 * Assert that the given String contains valid text content; that is, it must not
	 * be {@code null} and must contain at least one non-whitespace character.
	 * <pre class="code">Assert.hasText(name, "'name' must not be empty");</pre>
	 * @param text the String to check
	 * @param message the exception message to use if the assertion fails
	 * @throws IllegalArgumentException if the text does not contain valid text content
	 * @see StringUtils#hasText
	 */
	static void hasText(String str, String message) {
		checkArgument((str != null && !str.isEmpty() && CharMatcher.whitespace().negate().indexIn(str)>=0));
	}
	/**
	 * Convert a {@code String} array into a delimited {@code String} (e.g. CSV).
	 * <p>Useful for {@code toString()} implementations.
	 * @param arr the array to display (potentially {@code null} or empty)
	 * @param delim the delimiter to use (typically a ",")
	 * @return the delimited {@code String}
	 */
	static String arrayToDelimitedString(Object[] arr, String delim) {
		if ((arr == null || arr.length == 0)) {
			return "";
		}
		if (arr.length == 1) {
			return ObjectUtils.nullSafeToString(arr[0]);
		}
		return Joiner.on(delim).join(Iterables.transform(Arrays.asList(arr), STRING_VALUEOF_FUN));
	}

	/**
	 * Convert a {@code String} array into a comma delimited {@code String}
	 * (i.e., CSV).
	 * <p>Useful for {@code toString()} implementations.
	 * @param arr the array to display (potentially {@code null} or empty)
	 * @return the delimited {@code String}
	 */
	static String arrayToCommaDelimitedString(Object[] arr) {
		return arrayToDelimitedString(arr, ",");
	}
	static Class<?>[] nonnullTypesOf(Object... args){
		if(null != args) {
			Class<?>[] argTypes = new Class<?>[args.length];
			for(int i=0;i<argTypes.length;++i) {
				argTypes[i] = checkNotNull(args[i],"FAIL TO  get type of args[%s] caused by null",i).getClass();
			}
			return argTypes;
		}
		return EMPTY_CLASS_PARAMETERS;
	}
	static Class<?>[] nonnullTypesOf(Class<?>...argTypes){
		if(null != argTypes) {
			for(int i=0;i<argTypes.length;++i) {
				checkNotNull(argTypes[i],"argTypes[%s] is null",i).getClass();
			}
			return argTypes;
		}
		return EMPTY_CLASS_PARAMETERS;
	}

    /**
     * 获取非空的参数类型数组。<p>
     * 此方法用于根据传入的参数类型数组和参数数组，返回一个非空的参数类型数组。<br>
     * 如果传入的参数类型数组为 null，则根据参数数组推断参数类型；<br>
     * 如果参数数组也为 null，则返回一个空的参数类型数组。<br>
     * 如果传入的参数类型数组不为 null，则确保其中的每个元素都不为 null。<p>
     * 
     * @param parameterTypes 传入的参数类型数组，可以为 null。
     * @param args 传入的参数数组，可以为 null。
     * @return 非空的参数类型数组。
     */
    static Class<?>[] nonnullTypesOf(Class<?>[]parameterTypes, Object[]args){
        if (null == parameterTypes) {
            return null == args ? EMPTY_CLASS_PARAMETERS : nonnullTypesOf(args);
		} else {
            return nonnullTypesOf(parameterTypes);
		}
	}
    
    /** 如果参数数组不为 null，参数类型数组和参数数组长度必须一致 */
    static void checkParameterLength(Class<?>[]parameterTypes, Object[]args) {
		checkArgument(null == args || parameterTypes.length == args.length,
				"MISMATCH parameterTypes.length and args.length");	
    }
	/**
	 * 从注解中获取缓存配置注解<br>
	 * 优先查找 {@link AoCacheable}注解，如果没有找到，
	 * 则查找所有注解，
	 * 如果找到第一个将{@link AoCacheable}作为元注解的注解，
	 * 则将之与元注解一起合成为{@link AoCacheable}注解返回
	 * 仍然没有找到则返回{@code null}
	 * @param element 可注解实例
	 */
	static AoCacheable readAoCacheable(AnnotatedElement element) {
		/** 从注解中获取缓存配置 */
		AoCacheable annot = READ_AOCACHEABLE_FUN.apply(element);
		if(null != annot) {
			return annot;
		}
		return FluentIterable.from(element.getAnnotations())
				.transform(READ_AOCACHEABLE_FUN)
				.filter(Predicates.notNull())
				.first()
				.orNull();
	}

	private static final Function<Object, AoCacheable> READ_AOCACHEABLE_FUN =
		new Function<Object, AoCacheable>(){
				@Override
				public AoCacheable apply(Object input) {
					if(input instanceof Annotation) {
						AoCacheable annot = ((Annotation)input).annotationType().getAnnotation(AoCacheable.class);
						if(null!=annot) {
							CacheConfig meta= new CacheConfig(annot);
							meta.putAll(new AnnotationAttributes((Annotation)input));
							return meta.synthesize(AoCacheable.class);
						}
					}else if(input instanceof AnnotatedElement) {
						return ((AnnotatedElement)input).getAnnotation(AoCacheable.class);
					}
					return null;
				}};

    /**
     * <p>静态常量 {@code EXECUTABLE_MEMBER_CACHE} 是一个 {@code ThrowingFunctionCached} 类型的缓存实例。</p>
     * <p>该缓存用于存储可执行成员（方法或构造函数）的查找结果，以提高查找性能。</p>
     * <p>它以 {@code ExecutableMemberDescriptor} 作为键，{@code Member} 作为值。</p>
     * <p>通过缓存机制，避免了重复的反射查找操作，从而减少了性能开销。</p>
     */
    static final ThrowingFunctionCached<ExecutableMemberDescriptor, Member> EXECUTABLE_MEMBER_CACHE =
		ThrowingFunctionCached.builder()
		.weakValues().maximumSize(200).expireAfterAccess(60, TimeUnit.SECONDS)
		.getterFunction(new ThrowingFunction<ExecutableMemberDescriptor, Member, Throwable>() {
			@Override
			public Member apply(ExecutableMemberDescriptor input) {
				return getAccessibleExecutableMember0(input.clazz, input.methodName, input.parameterTypes);
			}
		}).build();


    /**
     * <p>此私有静态方法用于获取可访问的可执行成员（方法或构造函数）。</p>
     * <p>如果传入的 {@code clazz} 为 {@code null}，则会尝试从非空的 {@code target} 对象获取类信息。</p>
     * <p>参数类型数组 {@code parameterTypes} 会通过 {@code nonnullTypesOf} 方法处理，确保其非空。</p>
     * <p>若 {@code methodName} 为 {@code null} 或空字符串，则会调用 {@code getMatchingAccessibleConstructor} 方法查找构造函数；</p>
     * <p>否则，调用 {@code getMatchingAccessibleMethod} 方法查找方法。</p>
     * 
     * @param clazz 要查找可执行成员的类，可为 {@code null}。<br>
     * @param methodName 要查找的方法名，可为 {@code null} 或空字符串。<br>
     * @param parameterTypes 可执行成员的参数类型数组，可为 {@code null}。<br>
     * @return 找到的可访问的可执行成员（{@code Member} 类型），可能为 {@code Method} 或 {@code Constructor}。
     */
    private static Member getAccessibleExecutableMember0(Class<?> clazz, String methodName, Class<?>[] parameterTypes) {
		if (null == methodName || methodName.isEmpty()) {
			return getMatchingAccessibleConstructor(clazz, parameterTypes);
		}else{
            return getMatchingAccessibleMethod(clazz, methodName, parameterTypes);
        }
	}
    /**
     * <p>获取可访问的可执行成员（方法或构造函数）。</p>
     * <p>该方法会根据传入的类、方法名和参数类型信息，尝试查找对应的可访问的可执行成员。</p>
     * <p>如果 {@code methodName} 为 {@code null} 或空字符串，则会查找构造函数；否则，查找方法。</p>
     * <p>该方法会调用其他辅助方法进行实际的查找操作，并利用缓存机制提高性能。</p>
     * 
     * @param clazz 要查找可执行成员的类，可以为 {@code null}。如果为 {@code null}，会尝试从 {@code target} 获取类信息。<br>
     * @param methodName 要查找的方法名，可以为 {@code null} 或空字符串。<br>
     * @param parameterTypes 可执行成员的参数类型数组，可以为 {@code null}。<br>
     * @return 找到的可访问的可执行成员，可能是 {@code Method} 或 {@code Constructor} 类型。
     */
    static Member getAccessibleExecutableMember(Class<?> clazz, String methodName, Class<?>[] parameterTypes,
            Object target, Object[] args) {
    	if (clazz == null) {
    		clazz = checkNotNull(target, "clazz is null").getClass();
    	}
    	parameterTypes = nonnullTypesOf(parameterTypes, args);
        try {
			return EXECUTABLE_MEMBER_CACHE.get(new ExecutableMemberDescriptor(clazz, methodName, parameterTypes));
		} catch (Throwable e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
    }
    static Member getAccessibleExecutableMember(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
    	return getAccessibleExecutableMember(clazz, methodName, parameterTypes, null, null);
    }
    static Method getAccessibleMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
    	checkArgument(null != methodName && !methodName.isEmpty(), "methodName is null or empty");
        return (Method) getAccessibleExecutableMember(clazz, methodName, parameterTypes, null, null);
    }
    @SuppressWarnings("unchecked")
	static <T>Constructor<T> getAccessibleConstructor(Class<T> clazz, Class<?>... parameterTypes) {
    	return (Constructor<T>) getAccessibleExecutableMember(clazz, null, parameterTypes, null, null);
    }

    /**
     * <p>解析并返回可访问的可执行成员（方法或构造函数）。</p>
     * <p>该方法会根据传入的类、方法名和参数类型信息，尝试查找对应的可访问的可执行成员。</p>
     * <p>如果 {@code methodName} 为 {@code null} 或空字符串，则会查找构造函数；否则，查找方法。</p>
     * <p>若未找到匹配的可执行成员，将抛出 {@code NoSuchMethodException} 异常。</p>
     * NOTE: 与 {@link #getAccessibleExecutableMember(Class, String, Class[], Object, Object[])} 方法不同的是，
     * 该方法在未找到匹配的可执行成员时不会返回 {@code null}，而是直接抛出异常。
     *
     * @param clazz 要查找可执行成员的类，可以为 {@code null}。如果为 {@code null}，会尝试从 {@code target} 获取类信息。
     * @param methodName 要查找的方法名，可以为 {@code null} 或空字符串。
     * @param parameterTypes 可执行成员的参数类型数组，可以为 {@code null}。
     * @return 找到的可访问的可执行成员，可能是 {@code Method} 或 {@code Constructor} 类型。
     * @throws NoSuchMethodException 如果找不到匹配的可访问的可执行成员。
     */
    static Member resolveAccessibleExecutableMember(Class<?> clazz, String methodName, Class<?>[] parameterTypes,
    		Object target, Object[] args) throws NoSuchMethodException {
    	Member member = getAccessibleExecutableMember(clazz, methodName, parameterTypes, target, args);
    	if(null == member) {
    		if(null == methodName || methodName.isEmpty()) {
    			throw new NoSuchMethodException(
    					"No such accessible constructor: " + clazz.getName() + "." + toMemberString(null, parameterTypes));
    		}else {
    			throw new NoSuchMethodException(
    					"No such accessible method: " + clazz.getName() + "." + toMemberString(methodName, parameterTypes));
    		}
    	}
    	return member;
    }
    static Member resolveAccessibleExecutableMember(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
    	return resolveAccessibleExecutableMember(clazz, methodName, parameterTypes, null, null);
    }
    static Method resolveAccessibleMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
        checkArgument(null != methodName && !methodName.isEmpty(), "methodName is null or empty");
        return (Method) resolveAccessibleExecutableMember(clazz, methodName, parameterTypes, null, null);
    }
    @SuppressWarnings("unchecked")
	static <T>Constructor<T> resolveAccessibleConstructor(Class<T> clazz, Class<?>... parameterTypes) throws NoSuchMethodException {
    	return (Constructor<T>) resolveAccessibleExecutableMember(clazz, null, parameterTypes, null, null);
    }

    /**
     * <p>根据指定的类、参数类型、目标对象和参数数组创建一个新的实例。</p>
     * <p>该方法会通过反射机制查找并调用指定类的构造函数，使用传入的参数创建新的实例。</p>
     * NOTE:与 invokeConstructor 方法不同的是，该方法查找构造函数时使用缓存机制，以提高性能。</p>
     * @param <T> 要创建的实例的类型
     * @param clazz 要实例化的类
     * @param parameterTypes 构造函数的参数类型数组
     * @param target 目标对象，通常用于内部类的实例化，可能为 {@code null}
     * @param args 传递给构造函数的参数数组
     * @return 新创建的实例
     * @throws InstantiationException 如果指定的类是抽象类、接口、数组类、基本类型或者 {@code void}
     * @throws IllegalAccessException 如果构造函数不可访问
     * @throws InvocationTargetException 如果构造函数内部抛出异常
     * @throws NoSuchMethodException 如果找不到合适的构造函数
     */
	@SuppressWarnings("unchecked")
    static <T> T newInstance(Class<T> clazz, Class<?>[] parameterTypes, Object target, Object[] args)
			throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		Constructor<T> constructor = (Constructor<T>) resolveAccessibleExecutableMember(clazz, null, parameterTypes,
				target, args);
		return constructor.newInstance(args);
	}

    /**
     * <p>根据指定的类和参数数组创建一个新的实例。</p>
     * <p>该方法会通过反射机制，根据传入的参数推断构造函数的参数类型，</p>
     * <p>然后查找并调用指定类的构造函数，使用传入的参数创建新的实例。</p>
     * NOTE:与 invokeConstructor 方法不同的是，该方法查找构造函数时使用缓存机制，以提高性能。</p>
     * 
     * @param <T> 要创建的实例的类型
     * @param clazz 要实例化的类
     * @param args 传递给构造函数的参数数组
     * @return 新创建的实例
     * @throws InstantiationException 如果指定的类是抽象类、接口、数组类、基本类型或者 {@code void}
     * @throws IllegalAccessException 如果构造函数不可访问
     * @throws InvocationTargetException 如果构造函数内部抛出异常
     * @throws NoSuchMethodException 如果找不到合适的构造函数
     */
    static <T> T newInstance(Class<T> clazz, Object... args) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    	Constructor<T> constructor = resolveAccessibleConstructor(clazz, nonnullTypesOf(args));
    	return constructor.newInstance(args);
    }

    /**
     * <p>通过反射机制调用指定类中的方法。</p>
     * <p>该方法会根据传入的类、方法名、参数类型、目标对象和参数数组，查找并调用对应的方法。</p>
     * <p>调用过程中会使用缓存机制来查找可访问的方法，以提高性能。</p>
     * NOTE:与 invokeMethod 方法不同的是，该方法查找方法时使用缓存机制，以提高性能。</p>
     * @param <T> 方法返回值的类型
     * @param clazz 要调用方法的类
     * @param methodName 要调用的方法名，不能为 null 或空字符串
     * @param parameterTypes 方法的参数类型数组
     * @param target 调用方法的目标对象
     * @param args 传递给方法的参数数组
     * @return 调用方法后的返回值
     * @throws IllegalAccessException 如果该方法或其类不可访问
     * @throws InvocationTargetException 如果被调用的方法内部抛出异常
     * @throws NoSuchMethodException 如果找不到指定的方法
     * @see #resolveAccessibleExecutableMember(Class, String, Class[], Object, Object[])
     */
	@SuppressWarnings("unchecked")
    static <T> T invoke(Class<?> clazz, String methodName, Class<?>[] parameterTypes, T target, Object[] args)
			throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		checkArgument(null != methodName && !methodName.isEmpty(), "methodName is null or empty");
		Method method = (Method) resolveAccessibleExecutableMember(clazz, methodName, parameterTypes, target, args);
		return (T) method.invoke(target, args);
	}
    
    /**
     * <p>通过反射机制调用指定对象的方法。</p>
     * <p>该方法会根据传入的对象、方法名和参数数组，自动推断参数类型，然后查找并调用对应的方法。</p>
     * <p>调用过程中会使用缓存机制来查找可访问的方法，以提高性能。</p>
     * NOTE:与 invokeMethod 方法不同的是，该方法查找方法时使用缓存机制，以提高性能。</p>
     * @param <T> 方法返回值的类型
     * @param object 调用方法的目标对象，不能为 {@code null}
     * @param methodName 要调用的方法名，不能为 {@code null} 或空字符串
     * @param args 传递给方法的参数数组, 数组元素不能为 {@code null}
     * @return 调用方法后的返回值
     * @throws NoSuchMethodException 如果找不到指定的方法
     * @throws IllegalAccessException 如果该方法或其类不可访问
     * @throws InvocationTargetException 如果被调用的方法内部抛出异常
     */
	@SuppressWarnings("unchecked")
    static <T> T invoke(Object object, String methodName, Object... args)
			throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		Method method = resolveAccessibleMethod(checkNotNull(object, "object is null").getClass(), methodName,
				nonnullTypesOf(args));
		return (T) method.invoke(object, args);
	}

    /**
     * 可执行成员描述符类，用于描述可执行成员（方法或构造函数）的信息。<p>
     * 该类包含了查找可执行成员所需的关键信息，如类、方法名、参数类型、目标对象和参数数组。<br>
     * 它实现了 {@code hashCode} 和 {@code equals} 方法，以便用于缓存键的比较。<p>
     * 主要用于 {@code AocacheUtils} 类中的方法，通过这些信息来查找和缓存可执行成员。
     */
    private static class ExecutableMemberDescriptor {
    	private final Class<?> clazz;
    	private final String methodName;
    	private final Class<?>[] parameterTypes;
    	private ExecutableMemberDescriptor(Class<?> clazz, String methodName, Class<?>[] parameterTypes) {
    		this.clazz = clazz;
    		this.methodName = methodName;
    		this.parameterTypes = parameterTypes;	
    	}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + Arrays.hashCode(parameterTypes);
			result = prime * result + Objects.hash(clazz, methodName);
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj) {
				return true;
			}
			if (!(obj instanceof ExecutableMemberDescriptor)) {
				return false;
			}
			ExecutableMemberDescriptor other = (ExecutableMemberDescriptor) obj;
			return Objects.equals(clazz, other.clazz) && Objects.equals(methodName, other.methodName)
					&& Arrays.equals(parameterTypes, other.parameterTypes);
		}

    }
    /**
     * <p>检查给定的类型是否包含类型变量。</p>
     * <p>如果传入的可实例化类不为 null，在遇到类型变量时，会判定该类型变量是否属于该可实例化类。</p>
     * 
     * @param type 要检查的类型。
     * @param instantiableClass 可实例化的类对象。不为 null 时，会在遇到类型变量时判定类型变量是否属于该类。
     * @return 如果类型包含类型变量则返回 true，否则返回 false。
     */
    static boolean hasTypeVariable(Type type, Class<?> instantiableClass) {
		if(type instanceof Class<?>){
			return false;
		}
		TypeVariableChecker checker = new TypeVariableChecker(type, instantiableClass);
        return checker.hasTypeVariable;
    }

    /**
     * <p>这是一个静态内部类，继承自 {@code TypeVisitor}。</p>
     * <p>该类用于检查类型是否包含类型变量。</p>
     */
    private static class TypeVariableChecker extends TypeVisitor {
		final boolean hasTypeVariable;
		final Class<?> instantiableClass;

        /**
         * <p>构造一个 {@code TypeVariableChecker} 实例。</p>
         * <p>该构造函数接收一个类型对象和一个可实例化的类对象作为参数。</p>
         * <p>它会对传入的类型进行访问检查，判断是否存在类型变量。</p>
         * 
         * @param type 要检查的类型，不能为 {@code null}。
         * @param instantiableClass 可实例化的类对象。不为 {@code null} 时，会在遇到类型变量时判定类型变量是否属于 {@code instantiableClass}。
         */
        TypeVariableChecker(Type type, Class<?> instantiableClass) {
			this.instantiableClass = instantiableClass;
			boolean _hasTypeVariable = false;
			try {
				super.visit(checkNotNull(type, "type is null"));
				_hasTypeVariable = false;
			} catch (BreakException e) {
				_hasTypeVariable = true;
			}
			hasTypeVariable = _hasTypeVariable;
		}

		@Override
		public void visitTypeVariable(TypeVariable<?> typeVariable) {
			if (null == instantiableClass) {
				throw new BreakException();
			} else if (typeVariable.getGenericDeclaration() instanceof Class<?>) {
				Class<?> clazz = (Class<?>) typeVariable.getGenericDeclaration();
				if (clazz.isAssignableFrom(instantiableClass)) {
					throw new BreakException();
				}
			}
		}

		private class BreakException extends RuntimeException {
			private static final long serialVersionUID = 1L;

			private BreakException() {
				super("break");
			}
		}
	}
    /**
     * 复制自{@link com.google.common.reflect.TypeVisitor}<br>
     * Based on what a {@link Type} is, dispatch it to the corresponding {@code visit*} method. By
     * default, no recursion is done for type arguments or type bounds. But subclasses can opt to do
     * recursion by calling {@link #visit} for any {@code Type} while visitation is in progress. For
     * example, this can be used to reject wildcards or type variables contained in a type as in:
     *
     * <pre>   {@code
     *   new TypeVisitor() {
     *     protected void visitParameterizedType(ParameterizedType t) {
     *       visit(t.getOwnerType());
     *       visit(t.getActualTypeArguments());
     *     }
     *     protected void visitGenericArrayType(GenericArrayType t) {
     *       visit(t.getGenericComponentType());
     *     }
     *     protected void visitTypeVariable(TypeVariable<?> t) {
     *       throw new IllegalArgumentException("Cannot contain type variable.");
     *     }
     *     protected void visitWildcardType(WildcardType t) {
     *       throw new IllegalArgumentException("Cannot contain wildcard type.");
     *     }
     *   }.visit(type);}</pre>
     *
     * <p>One {@code Type} is visited at most once. The second time the same type is visited, it's
     * ignored by {@link #visit}. This avoids infinite recursion caused by recursive type bounds.
     *
     * <p>This class is <em>not</em> thread safe.
     *
     * @author Ben Yu
     */
    private static abstract class TypeVisitor {

      private final Set<Type> visited = Sets.newHashSet();

      /**
       * Visits the given types. Null types are ignored. This allows subclasses to call
       * {@code visit(parameterizedType.getOwnerType())} safely without having to check nulls.
       */
      public final void visit(Type... types) {
        for (Type type : types) {
          if (type == null || !visited.add(type)) {
            // null owner type, or already visited;
            continue;
          }
          boolean succeeded = false;
          try {
            if (type instanceof TypeVariable) {
              visitTypeVariable((TypeVariable<?>) type);
            } else if (type instanceof WildcardType) {
              visitWildcardType((WildcardType) type);
            } else if (type instanceof ParameterizedType) {
              visitParameterizedType((ParameterizedType) type);
            } else if (type instanceof Class) {
              visitClass((Class<?>) type);
            } else if (type instanceof GenericArrayType) {
              visitGenericArrayType((GenericArrayType) type);
            } else {
              throw new AssertionError("Unknown type: " + type);
            }
            succeeded = true;
          } finally {
            if (!succeeded) { // When the visitation failed, we don't want to ignore the second.
              visited.remove(type);
            }
          }
        }
      }

      protected void visitClass(Class<?> t) {}

      protected void visitGenericArrayType(GenericArrayType t) {
    	  visit(t.getGenericComponentType());
    	  
      }

      protected void visitParameterizedType(ParameterizedType t) {
    	  visit(t.getActualTypeArguments());
    	  visit(t.getRawType());
      }

      protected void visitTypeVariable(TypeVariable<?> t) {
    	  visit(t.getBounds());
      }

      protected void visitWildcardType(WildcardType t) {
    	  visit(t.getLowerBounds());
    	  visit(t.getUpperBounds());
      }
    }
	/**
	 * <p>
	 * 基于传入的类对象 {@code clazz} 和目标类型数组 {@code typesOfTarget}，解析目标类型为完整类型并返回对应的
	 * {@link TypeToken} 实例数组。
	 * </p>
	 * <p>
	 * 例如，对于泛型类的类型变量（如 T），会尝试解析并返回对应的类型。
	 * </p>
	 * <p>
	 * 该方法会根据传入的参数从缓存中获取对应的 {@code TypeToken} 数组。
	 * </p>
	 * 
	 * @param clazz         目标类型所属的类，不能为 {@code null}。
	 * @param typesOfTarget 目标类型数组，不能为 {@code null}。
	 * @return 解析后的 {@code TypeToken} 实例数组。
	 */
    static TypeToken<?>[] resolveTypeToken(Class<?> clazz, Type... typesOfTarget) {
        TypeToken<?> token = TypeToken.of(checkNotNull(clazz, "clazz is null"));
        TypeToken<?>[] typeTokens = new TypeToken<?>[checkNotNull(typesOfTarget, "typesOfTarget is null").length];
        for (int i = 0; i < typeTokens.length; i++) {
            if(hasTypeVariable(typesOfTarget[i], clazz)){
                checkNotNull(typesOfTarget[i], "typesOfTarget[%s] is null", i);
                typeTokens[i] = token.resolveType(typesOfTarget[i]);
            }else {
                typeTokens[i] = TypeToken.of(typesOfTarget[i]);
            }
		}
        return typeTokens;
	}

	/**
	 * <p>
	 * 基于目标类型所属的类，解析目标类型为完整类型并返回对应的 {@link TypeToken} 实例。
	 * </p>
	 * 比如：对于泛型类的类型变量(如 T)，会尝试解析并返回对应的类型。
	 * <p>
	 * <p>
	 * 该方法会根据传入的类对象 {@code clazz} 和目标类型 {@code typeOfTarget} 从缓存中获取对应的
	 * {@code TypeToken}。
	 * </p>
	 * 
	 * @param clazz        目标类型所属的类，不能为 {@code null}。
	 * @param typeOfTarget 目标类型，不能为 {@code null}。
	 * @return 解析后的 {@code TypeToken} 实例。
	 */
	static TypeToken<?> resolveTypeToken(Class<?> clazz, Type typeOfTarget) {
		if (!hasTypeVariable(typeOfTarget, clazz)) {
			return TypeToken.of(typeOfTarget);
		}
		return TypeToken.of(checkNotNull(clazz, "clazz is null"))
				.resolveType(checkNotNull(typeOfTarget, "typeOfTarget is null"));
	}
	static Type resolveType(Class<?> clazz, Type typeOfTarget) {
		return resolveTypeToken(clazz, typeOfTarget).getType();
	}

	static Type[] resolveTypes(Class<?> clazz, Type... typesOfTarget) {
		TypeToken<?> token = TypeToken.of(checkNotNull(clazz, "clazz is null"));
    	Type[] types = Arrays.copyOf(typesOfTarget, checkNotNull(typesOfTarget, "typesOfTarget is null").length); // 复制数组，避免直接修改原始数组长
    	for (int i = 0; i < types.length; i++) {
			Type type = types[i];
			if (hasTypeVariable(type, clazz)) {
				types[i] = token.resolveType(checkNotNull(type, "typesOfTarget[%s] is null", i)).getType();
			}
    	}
    	return types;
	}

	static Type resolveType(Object target, Type typeOfTarget) {
		return null == target ? typeOfTarget : resolveType(target.getClass(), typeOfTarget);
	}

	static Type[] resolveTypes(Object target, Type... typesOfTarget) {
		return null == target ? typesOfTarget : resolveTypes(target.getClass(), typesOfTarget);
	}
}
